/*
 * Licensed to the Tai Ping Jin Ke
 *
 * Copyright (c) 2022 .
 * All rights reserved.
 * 项目名称：太平金科-基础组件封装层-SpringBoot组件封装-核心依赖
 * 版权说明：本软件属太平金融科技服务(上海)有限公司所有，在未获得太平金融科技服务(上海)有限公司正式授权情况下，任何企业和个人，不能获取、阅读、安装、传播本软件涉及的任何受知识产权保护的内容。
 */
package com.taipingframework.boot.web.response.result;

import cn.hutool.json.JSONUtil;

import java.time.LocalDateTime;
import java.io.Serializable;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.taipingframework.boot.web.response.status.ApiStatusEnum;
import com.taipingframework.utility.extend.ReactorHelper;
import com.taipingframework.utility.web.IApiResult;
import com.taipingframework.utility.web.IApiStatus;
import com.taipingframework.utility.constant.ApplicationConstant;
import com.taipingframework.utility.thread.threadlocal.TransmittableThreadLocalHelper;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.apache.commons.lang3.StringUtils;
import reactor.core.publisher.Mono;

/**
 * api 返回对象的声明
 *
 * @param <T> 实际返回的开放接口层数据对象
 * <p>
 * 使用方式：
 * ApiResult<T> apiResult = ApiResult.<T>build()
 * 		.setStatus(ApiStatusEnum.SUCCESS)
 * 		.setResult(t);
 */
@Setter
@Accessors(chain = true)
@SuppressWarnings({"unused"})
public final class ApiResult<T extends Object & Serializable> implements Serializable, IApiResult<T> {

    private static final long serialVersionUID = 831043606552L;

    /**
     * 请求标识
     */
    private String requestId;

    /**
     * api 状态码
     */
    @JsonProperty("errcode")
    private String code;

    /**
     * 请求成功 - "ok"，请求失败 - 错误消息
     */
    @JsonProperty("errmsg")
    private String message;

    /**
     * 响应结果的补充说明，后端是否按照预期完成请求，true - 后端认为返回了正确的结果，false - 后端认为返回了错误的结果。
     */
    private Boolean success;

    /**
     * 网络服务的响应数据
     */
    private T result;

    /**
     * 请求结束返回结果时的时间戳，需要排查异常情况时，能够帮助程序员迅速定位到发生错误的时间节点，禁止修改
     */
    private final LocalDateTime timestamp = LocalDateTime.now();

    private ApiResult() {
    }

    private ApiResult(String requestId) {
        if (StringUtils.isNotEmpty(requestId)) {
            setRequestId(requestId);
        }
    }

    /**
     * 构建 常规api 的返回对象
     */
    public static <T extends Serializable> ApiResult<T> build() {
        String cachedRequestId = TransmittableThreadLocalHelper.getEnvironment().get(ApplicationConstant.RESPONSE_IDENTITY);
        return new ApiResult<>(cachedRequestId);
    }

    /**
     * 构建 常规api 的返回对象
     *
     * @param requestId 请求标识
     */
    public static <T extends Serializable> ApiResult<T> build(String requestId) {
        return new ApiResult<>(requestId);
    }

    /**
     * 构建 常规api 的返回对象
     */
    public static <T extends Serializable> Mono<ApiResult<T>> create() {
        return ReactorHelper.getContext()
                .map(context -> {
                    String reqId = StringUtils.EMPTY;
                    if (context.hasKey(ApplicationConstant.RESPONSE_IDENTITY)) {
                        reqId = context.get(ApplicationConstant.RESPONSE_IDENTITY);
                    }
                    return reqId;
                })
                .map(ApiResult::new);
    }

    /**
     * 主动填充当前请求的唯一标识
     */
    @Override
    public String getRequestId() {
        return requestId;
    }

    @Override
    public String getCode() {
        return code;
    }

    @Override
    public String getMessage() {
        return message;
    }

    /**
     * 主动判断响应结果result是否可用
     */
    @Override
    public Boolean getSuccess() {
        setSuccess(ApiStatusEnum.SUCCESS.getCode().equalsIgnoreCase(code));
        return success;
    }

    @Override
    public T getResult() {
        // 浅克隆，防止对象被篡改
        // return ObjectUtil.clone(data);

        // 浅克隆
        // return data.clone();

        // 如果要更新对象，请调用对象内部显式声明的setter方法
        // 注意：执行深克隆时，该类以及该类的成员变量所属的类都需要实现Serializable接口
        return result;
    }

    @Override
    public LocalDateTime getTimestamp() {
        return timestamp;
    }

    @Override
    public String toString() {
        return JSONUtil.toJsonStr(this);
    }

    /**
     * 设置 微服务 响应状态
     */
    public ApiResult<T> setStatus(IApiStatus status) {
        setCode(status.getCode());
        setMessage(status.getMessage());
        return this;
    }

    /**
     * 强制要求使用枚举设置 响应码
     */
    private void setCode(String code) {
        this.code = code;
    }

    /**
     * 强制要求使用枚举设置 响应码描述
     */
    private void setMessage(String message) {
        this.message = message;
    }

    /**
     * 不允许外部主动更改 属性requestId
     */
    private void setRequestId(String requestId) {
        this.requestId = requestId;
    }

    /**
     * 不允许外部主动更改 属性success
     */
    private void setSuccess(Boolean success) {
        this.success = success;
    }

}
